library saas_warning_package;

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:dio_net_work/dio_net_work.dart';
import 'package:dio_net_work/http_utils.dart';
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:web_socket_client/web_socket_client.dart';

typedef OnReceiveCallListener = Function(CallWarningData data);
typedef OnReceiveCallOriListener = Function(dynamic data);
typedef OnConnectionStateListener = Function(ConnectState state,String msg);

enum ConnectState{
  DISCONNECT,
  CONNECTING,
  RECONNECT,
  CONNECTED
}


class SaasWarningPackage {
  static SaasWarningPackage? _instance;

  static SaasWarningPackage get instance => _instance ??= SaasWarningPackage();

  OnReceiveCallListener? _receiveWarningCall;
  OnReceiveCallListener? _receiveWarningHandle;
  OnReceiveCallOriListener? _receiveMapInfo;

  List<OnConnectionStateListener> _connectionStateListeners = [];

  void addConnectionStateListener(OnConnectionStateListener listener){
    _connectionStateListeners.add(listener);
  }

  void removeConnectStateListener(OnConnectionStateListener listener){
    _connectionStateListeners.remove(listener);
  }

  void setReceiveListener(
      {OnReceiveCallListener? receiveWarningCall,
      OnReceiveCallListener? receiveWarningHandle,
      OnReceiveCallOriListener? receiveMapInfo}) {
    _receiveMapInfo = receiveMapInfo;
    _receiveWarningHandle = receiveWarningHandle;
    _receiveWarningCall = receiveWarningCall;
  }



  String _baseUrl = "";

  String _branchId = "";

  String _account = "";

  ///重试时间
  int _retrySecond = 5;

  String get baseUrl => _baseUrl;

  String get _socketUrl =>
      "${_baseUrl.replaceAll("https", "wss").replaceAll("http", "ws")}websocket";

  String get _getUuid => "${baseUrl}callInfo/bigScreenUUID";

  bool _disposed = false;

  void initUrl(String url, {String account = ""}) {
    _baseUrl = url;
    _account = account;
  }

  void initBranchId(String branchId) {
    _branchId = branchId;
  }

  Future<void> changeBranchId(String branchId) async {
    _branchId = branchId;
    closeSocket();
    await connectSocket();
  }

  void setRetrySecond(int second) {
    _retrySecond = second;
  }

  CancelToken _cancelToken = CancelToken();

  WebSocket? socket;

  void closeSocket() {
    socket?.close(WebSocketStatus.normalClosure, 'cancel');
    // socket?.clearListener();
    // socket = null;
  }

  Timer? _timer;

  void _retry() {
    if (_timer != null) {
      _timer?.cancel();
      _timer = null;
    }
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      debugPrint(timer.tick.toString());
      for(var listener in _connectionStateListeners){
        listener(ConnectState.RECONNECT,"${_retrySecond-timer.tick}秒后重连${"."*timer.tick}");
      }
      if (timer.tick >= _retrySecond) {
        timer.cancel();
        connectSocket();
      }
    });
  }

  ///获取websocket uuid
  Future<String> getUUid(String branchId) async {
    var map = {
      "branchId": branchId,
    };
    try {
      var data = await HttpGo.instance
          .getDataString(_getUuid, data: map, cancelToken: _cancelToken);
      return data;
    } catch (e) {
      debugPrint(e.toString());
      _retry();
      return "";
    }
  }

  Future<void> connectSocket() async {
    if (socket?.connection.state is Connected) {
      debugPrint("已连接");
      for(var listener in _connectionStateListeners){
        listener(ConnectState.CONNECTED,"已连接");
      }
      return;
    }
    _disposed = false;
    var uuid = await getUUid(_branchId);
    if (uuid.isEmpty) {
      return;
    }
    if (_disposed) {
      return;
    }

    var url = "${_socketUrl}?param=guest$uuid&account=${_account}";
    debugPrint(url);
    var uri = Uri.parse(url);
    socket = WebSocket(
      uri,
      pingInterval: const Duration(seconds: 10),
    );
    _initListener();
  }

  void _initListener() {
    socket?.connection.listen((event) {
      if (event is Connected) {
        //todo:已连接
        debugPrint("${DateTime.now()}:已连接");
        for(var listener in _connectionStateListeners){
          listener(ConnectState.CONNECTED,"已连接");
        }
        _timer?.cancel();
        sendHeart();
      }

      if (event is Disconnected) {
        socket?.clearListener();
        socket = null;
        debugPrint("${DateTime.now()}:断开连接");
        for(var listener in _connectionStateListeners){
          listener(ConnectState.DISCONNECT,"断开连接");
        }
        if (_disposed) {
          return;
        }
        _retry();
      }

      if (event is Connecting) {
        //todo:正在连接中
        debugPrint("${DateTime.now()}:正在连接中");
        for(var listener in _connectionStateListeners){
          listener(ConnectState.CONNECTING,"正在连接中");
        }
      }

      if (event is Disconnecting) {
        //todo:正在断开连接
        debugPrint("${DateTime.now()}:正在断开连接");
      }
    });

    socket?.messages.listen((data) {
      ///接收报警信息
      Map map;
      if(data is String){
        map = jsonDecode(data);
      }else{
        map = data;
      }
      if (_receiveMapInfo != null) {
        _receiveMapInfo!(map);
      }
      var type = map['bizType'];

      debugPrint("${DateTime.now()}:receiveData==${map}");

      ///收到待处理报警
      if (1 == type) {
        if (map['params'] == null) {
          return;
        }
        var msg = map['params']['content'];
        if ((msg).isNotEmpty) {
          var text = changeNumber(msg);
          var warningBuilding = map['callInfo']['buildingId'];
          var warningFloor = map['callInfo']['floorId'];
          var warningId = map['callInfo']['id'];
          var callType = map['callInfo']['callTypeName'];
          var devicesId = map['callInfo']['deviceId'];
          var id = map['callInfo']['id'];
          var speechText = text;

          CallWarningData callData = CallWarningData(
              msg: msg,
              buildingId: warningBuilding,
              floorId: warningFloor,
              eventId: warningId,
              callTypeName: callType,
              devicesId: devicesId,
              id: id,
              speechMsg: speechText,
              callType: map['callInfo']['callType'],
              alarmType: map['callInfo']['alarmType'],
              alarmTypeName: map['callInfo']['alarmTypeName'],
          );

          if (_receiveWarningCall != null) {
            _receiveWarningCall!(callData);
          }
        }
      }

      ///收到已处理信息
      if (2 == type) {
        var warningBuilding = map['callInfo']['buildingId'];
        var warningFloor = map['callInfo']['floorId'];
        var warningId = map['callInfo']['id'];
        var callType = map['callInfo']['callTypeName'];
        var devicesId = map['callInfo']['deviceId'];
        var id = map['callInfo']['id'];
        CallWarningData callData = CallWarningData(
            msg: "",
            buildingId: warningBuilding,
            floorId: warningFloor,
            eventId: warningId,
            callTypeName: callType,
            devicesId: devicesId,
            id: id,
            speechMsg: "",
            callType: map['callInfo']['callType'],
            alarmType: map['callInfo']['alarmType'],
          alarmTypeName: map['callInfo']['alarmTypeName'],
        );
        if (_receiveWarningHandle != null) {
          _receiveWarningHandle!(callData);
        }
      }
    });
  }

  String changeNumber(String msg) {
    return msg
        .replaceAll("0", "零")
        .replaceAll("1", "一")
        .replaceAll("2", "二")
        .replaceAll("3", "三")
        .replaceAll("4", "四")
        .replaceAll("5", "五")
        .replaceAll("6", "六")
        .replaceAll("7", "七")
        .replaceAll("8", "八")
        .replaceAll("9", "九");
  }

  void sendHeart() {
    if (socket?.connection.state is Connected) {
      socket?.send(jsonEncode({"type": "pong", "data": ""}));
      debugPrint("${DateTime.now()}:sendHeart");
      Future.delayed(const Duration(seconds: 10), () {
        sendHeart();
      });
    } else {
      debugPrint("${DateTime.now()}:notsendHeart${socket?.connection.state}");
    }
  }

  void dispose() {
    _disposed = true;
    closeSocket();
    _timer?.cancel();
    _timer = null;
    _receiveMapInfo = null;
    _receiveWarningHandle = null;
    _receiveWarningCall = null;
  }
}

class CallWarningData {
  String msg;
  dynamic buildingId;
  dynamic floorId;
  dynamic eventId;
  String? callTypeName;
  num? devicesId;
  num? id;
  String? speechMsg;
  num? callType;
  num? alarmType;
  String? alarmTypeName;

  CallWarningData(
      {required this.msg,
      required this.buildingId,
      required this.floorId,
      required this.eventId,
      required this.callTypeName,
      required this.devicesId,
      required this.id,
      required this.speechMsg,
      required this.callType,
      this.alarmType,
      this.alarmTypeName});

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is CallWarningData &&
          runtimeType == other.runtimeType &&
          id == other.id;

  @override
  int get hashCode => id.hashCode;

  Map<String, dynamic> toMap() {
    return {
      'msg': this.msg,
      'buildingId': this.buildingId,
      'floorId': this.floorId,
      'eventId': this.eventId,
      'callTypeName': this.callTypeName,
      'devicesId': this.devicesId,
      'id': this.id,
      'speechMsg': this.speechMsg,
      'callType': this.callType,
      'alarmType': this.alarmType,
      'alarmTypeName': this.alarmTypeName,
    };
  }

  factory CallWarningData.fromMap(Map<String, dynamic> map) {
    return CallWarningData(
      msg: map['msg'] ,
      buildingId: map['buildingId'] ,
      floorId: map['floorId'] ,
      eventId: map['eventId'] ,
      callTypeName: map['callTypeName'] ,
      devicesId: map['devicesId'] ,
      id: map['id'] ,
      callType: map['callType'],
      speechMsg: map['speechMsg'] ,
      alarmType: map['alarmType'],
      alarmTypeName: map['alarmTypeName'],
    );
  }
}
